// SystemPage.cpp

#include "StdAfx.h"
#include "SystemPage.h"

#include "Common/String/StringConvert.h"

#include "Common/System/Defs.h"
#include "Common/FileSystem/FileFind.h"

#include "Common/FileSystem/IFolder.h"
#include "HelpUtils.h"
#include "Common/Lang/LangUtils.h"
#include "Common/Plugin/PluginLoader.h"
#include "Common/App/ProgramLocation.h"
#include "Common/String/StringUtils.h"

#include "resource.h"
#include "Common/Agent/Agent.h"



using namespace Common;
using namespace Registry;

static Lang::CIDLangPair kIDLangPairs[] =
{
  { IDC_SYSTEM_STATIC_ASSOCIATE,  0x03010302},
  { IDC_SYSTEM_SELECT_ALL,        0x03000330}
};

static LPCTSTR kSystemTopic = _T("FM/options.htm#system");

LRESULT CSystemPage::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
  _initMode = true;
  LangSetDlgItemsText(HWND(*this), kIDLangPairs, sizeof(kIDLangPairs) / sizeof(kIDLangPairs[0]));

  _listViewExt.Attach(GetDlgItem(IDC_SYSTEM_LIST_ASSOCIATE));
  _listViewPlugins.Attach(GetDlgItem(IDC_SYSTEM_LIST_PLUGINS));

  /*
  CheckButton(IDC_SYSTEM_INTEGRATE_TO_CONTEXT_MENU,
      NRegistryAssociations::CheckContextMenuHandler());
  */

  UINT32 newFlags = LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT;
  _listViewExt.SetExtendedListViewStyle(newFlags, newFlags);
  _listViewPlugins.SetExtendedListViewStyle(newFlags, newFlags);

  _listViewExt.InsertColumn(0, Lang::LangString(IDS_PROP_EXTENSION, 0x02000205), 40);
  const CString s = Lang::LangString(IDS_PLUGIN, 0x03010310);
  _listViewExt.InsertColumn(1, s, 40);

  _listViewPlugins.InsertColumn(0, s, 40);

  _extDatabase.Read();

  for (size_t i = 0; i < _extDatabase.ExtBigItems.size(); i++)
  {
    auto &extInfo = _extDatabase.ExtBigItems[i];

    int itemIndex = _listViewExt.InsertItem(i, (LPCWSTR)extInfo->Ext);
	_listViewExt.SetItemData(itemIndex, i);

    CString iconPath;
    int iconIndex;
    extInfo->Associated = CheckShellExtensionInfo(String::GetSystemString(extInfo->Ext), iconPath, iconIndex);
    if (extInfo->Associated && !Common::FileSystem::Find::DoesFileExist(iconPath))
      extInfo->Associated = false;
    _listViewExt.SetCheckState(itemIndex, extInfo->Associated);

    SetMainPluginText(itemIndex, i);
  }
  // _listViewExt.SortItems();
  
  if (_listViewExt.GetItemCount() > 0)
    _listViewExt.SetItemState(0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
  RefreshPluginsList(-1);
  _initMode = false;

  return TRUE;
}

void CSystemPage::SetMainPluginText(int itemIndex, int indexInDatabase)
{
	_listViewExt.SetItemText(itemIndex, 1, _extDatabase.GetMainPluginNameForExtItem(indexInDatabase));
}

static CString GetProgramCommand()
{
  CString path = _T("\"");
  CString folder;
  if (App::GetProgramFolderPath(folder))
    path += folder;
  path += _T("7zFM.exe\" \"%1\"");
  return path;
}

static CString GetIconPath(const CString &filePath,
    const CLSID &clsID, const CString &extension, INT32 &iconIndex)
{
	Plugin::CPluginLibrary library;
  CComPtr<IFolderManager> folderManager;
  CComPtr<IFolderFolder> folder;
  if (filePath.IsEmpty())
  {
	Agent::CArchiveFolderManager * manager = new CComObject<Agent::CArchiveFolderManager>;
	manager->FinalConstruct();
    folderManager = manager;	
  }
  else if (library.LoadAndCreateManager(filePath, clsID, &folderManager) != S_OK)
    return CString();
  CComBSTR extBSTR;
  if (folderManager->GetExtensions(&extBSTR) != S_OK)
    return CString();
  const CString ext2 = CW2T((LPCWSTR) (const wchar_t *)extBSTR);
  CStringVector exts;
  String::SplitStringBySpace(ext2, exts);
  for (size_t i = 0; i < exts.size(); i++)
  {
    const CString &plugExt = exts[i];
	if (extension.CompareNoCase((LPCTSTR) plugExt) == 0)
    {
      CComBSTR iconPathTemp;
      if (folderManager->GetIconPath(plugExt, &iconPathTemp, &iconIndex) != S_OK)
        break;
      if (iconPathTemp != 0)
        return (const wchar_t *)iconPathTemp;
    }
  }
  return CString();
}

int CSystemPage::OnApply()
{
  UpdateDatabase();
  _extDatabase.Save();
  
  for (size_t i = 0; i < _extDatabase.ExtBigItems.size(); i++)
  {
    const auto &extInfo = _extDatabase.ExtBigItems[i];
    if (extInfo->Associated)
    {
      CString title = extInfo->Ext + CString(_T(" Archive"));
      CString command = GetProgramCommand();
      CString iconPath;
      INT32 iconIndex = -1;
	  if (!extInfo->PluginsPairs.empty())
      {
        const auto &plugin = _extDatabase.Plugins[extInfo->PluginsPairs[0].Index];
        iconPath = GetIconPath(plugin->FilePath, plugin->ClassID, extInfo->Ext, iconIndex);
      }
      Registry::AddShellExtensionInfo(String::GetSystemString(extInfo->Ext),
            title, command, iconPath, iconIndex, NULL, 0);
    }
    else
      Registry::DeleteShellExtensionInfo(String::GetSystemString(extInfo->Ext));
  }
  /*
  if (IsButtonCheckedBool(IDC_SYSTEM_INTEGRATE_TO_CONTEXT_MENU))
    NRegistryAssociations::AddContextMenuHandler();
  else
    NRegistryAssociations::DeleteContextMenuHandler();
  */
  #ifndef UNDER_CE
  SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
  WasChanged = true;
  #endif
  //return PSNRET_NOERROR;
  return TRUE;
}

void CSystemPage::OnHelp()
{
  ShowHelpWindow(NULL, kSystemTopic);
}

void CSystemPage::SelectAll()
{
  int count = _listViewExt.GetItemCount();
  for (int i = 0; i < count; i++)
    _listViewExt.SetCheckState(i, true);
  UpdateDatabase();
}

LRESULT CSystemPage::OnSelectAll(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
	SelectAll();
	SetModified();
	return TRUE;
}


LRESULT CSystemPage::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	NMHDR* pNMHDR = (NMHDR*)lParam;
  if (pNMHDR->hwndFrom == HWND(_listViewExt))
  {
    switch(pNMHDR->code)
    {
      case (LVN_ITEMCHANGED):
        return OnItemChanged((const NMLISTVIEW *)lParam);
      case NM_RCLICK:
      case NM_DBLCLK:
      case LVN_KEYDOWN:
      case NM_CLICK:
      case LVN_BEGINRDRAG:
        PostMessage(kRefreshpluginsListMessage, 0);
        PostMessage(kUpdateDatabase, 0);
        break;
    }
  }
  else if (pNMHDR->hwndFrom == HWND(_listViewPlugins))
  {
    switch(pNMHDR->code)
    {
      case NM_RCLICK:
      case NM_DBLCLK:
      // case LVN_KEYDOWN:
      case NM_CLICK:
      case LVN_BEGINRDRAG:
        PostMessage(kUpdateDatabase, 0);
        break;

      case (LVN_ITEMCHANGED):
      {
        OnItemChanged((const NMLISTVIEW *)lParam);
        PostMessage(kUpdateDatabase, 0);
        break;
      }
      case LVN_KEYDOWN:
      {
        OnPluginsKeyDown((LPNMLVKEYDOWN)lParam);
        PostMessage(kUpdateDatabase, 0);
        break;
      }
    }
  }
  return baseClass::OnNotify(uMsg, wParam, lParam, bHandled);
}

bool CSystemPage::OnPluginsKeyDown(LPNMLVKEYDOWN keyDownInfo)
{
  bool alt = (::GetKeyState(VK_MENU) & 0x8000) != 0;
  // bool ctrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0;
  switch(keyDownInfo->wVKey)
  {
    case VK_UP:
    {
      if (alt)
        MovePlugin(true);
      return true;
    }
    case VK_DOWN:
    {
      if (alt)
        MovePlugin(false);
      return true;
    }
  }
  return false;
}

void CSystemPage::MovePlugin(bool upDirection)
{
  int selectedPlugin = _listViewPlugins.GetSelectionMark();
  if (selectedPlugin < 0)
    return;
  int newIndex = selectedPlugin + (upDirection ? -1: 1);
  if (newIndex < 0 || newIndex >= _listViewPlugins.GetItemCount())
    return;
  int selectedExtIndex = GetSelectedExtIndex();
  if (selectedExtIndex < 0)
    return;
  auto &extInfo = _extDatabase.ExtBigItems[selectedExtIndex];
  Plugin::CPluginEnabledPair pluginPairTemp = extInfo->PluginsPairs[newIndex];
  extInfo->PluginsPairs[newIndex] = extInfo->PluginsPairs[selectedPlugin];
  extInfo->PluginsPairs[selectedPlugin] = pluginPairTemp;

  SetMainPluginText(_listViewExt.GetSelectionMark(), selectedExtIndex);
  RefreshPluginsList(newIndex);

  SetModified();
}

bool CSystemPage::OnItemChanged(const NMLISTVIEW *info)
{
  if (_initMode)
    return true;
  if ((info->uChanged & LVIF_STATE) != 0)
  {
    UINT oldState = info->uOldState & LVIS_STATEIMAGEMASK;
    UINT newState = info->uNewState & LVIS_STATEIMAGEMASK;
    if (oldState != newState)
      SetModified();
  }
  // PostMessage(kRefreshpluginsListMessage, 0);
  // RefreshPluginsList();
  return true;
}

void CSystemPage::UpdateDatabase()
{
  int i;
  for (i = 0; i < _listViewExt.GetItemCount(); i++)
  {
	  LPARAM param = _listViewExt.GetItemData(i);
    if (NULL == param)
      return;
    auto &extInfo = _extDatabase.ExtBigItems[(int)param];
    extInfo->Associated = _listViewExt.GetCheckState(i);
  }

  int selectedExtIndex = GetSelectedExtIndex();
  if (selectedExtIndex < 0)
    return;

  auto &extInfo = _extDatabase.ExtBigItems[selectedExtIndex];
  for (i = 0; i < _listViewPlugins.GetItemCount(); i++)
  {
    extInfo->PluginsPairs[i].Enabled = _listViewPlugins.GetCheckState(i);
  }
}



int CSystemPage::GetSelectedExtIndex()
{
  int selectedIndex = _listViewExt.GetSelectionMark();
  if (selectedIndex < 0)
    return -1;
  LPARAM param = _listViewExt.GetItemData(selectedIndex);
  if (NULL == param)
    return -1;
  return (int)param;
}


void CSystemPage::RefreshPluginsList(int selectIndex)
{
  _listViewPlugins.DeleteAllItems();
  int selectedExtIndex = GetSelectedExtIndex();
  if (selectedExtIndex < 0)
    return;
  const auto &extInfo = _extDatabase.ExtBigItems[selectedExtIndex];

  _initMode = true;
  for (size_t i = 0; i < extInfo->PluginsPairs.size(); i++)
  {
    Plugin::CPluginEnabledPair pluginPair = extInfo->PluginsPairs[i];
    int itemIndex = _listViewPlugins.InsertItem(i, _extDatabase.Plugins[pluginPair.Index]->Name);
	_listViewPlugins.SetItemData(itemIndex, i);
    _listViewPlugins.SetCheckState(itemIndex, pluginPair.Enabled);
  }
  if (_listViewPlugins.GetItemCount() > 0)
  {
    if (selectIndex < 0)
      selectIndex = 0;
    _listViewPlugins.SetItemState(selectIndex, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
  }
  _initMode = false;
}



/*
static BYTE kZipShellNewData[] =
  { 0x50-1, 0x4B, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0 };

static BYTE kRarShellNewData[] =
  { 0x52-1, 0x61, 0x72, 0x21, 0x1A, 7, 0, 0xCF, 0x90, 0x73, 0, 0, 0x0D, 0, 0, 0, 0, 0, 0, 0};

class CSignatureMaker
{
public:
  CSignatureMaker()
  {
    kZipShellNewData[0]++;
    kRarShellNewData[0]++;
  };
};

static CSignatureMaker g_SignatureMaker;
*/
